package com.fourforfo.fourmall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.fourforfo.common.to.ProductSkuInfoTO;
import com.fourforfo.common.to.SkuStockTO;
import com.fourforfo.common.to.mq.OrderTO;
import com.fourforfo.common.to.mq.StockDetailTO;
import com.fourforfo.common.to.mq.StockLockedTO;
import com.fourforfo.common.utils.R;
import com.fourforfo.fourmall.ware.config.WareRollbackMQConfig;
import com.fourforfo.fourmall.ware.entity.*;
import com.fourforfo.fourmall.ware.exception.NoStockException;
import com.fourforfo.fourmall.ware.feign.OrderFeignService;
import com.fourforfo.fourmall.ware.feign.ProductFeignService;
import com.fourforfo.fourmall.ware.service.WareOrderTaskDetailService;
import com.fourforfo.fourmall.ware.service.WareOrderTaskService;
import com.fourforfo.fourmall.ware.vo.*;
import com.rabbitmq.client.Channel;
import lombok.Data;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fourforfo.common.utils.PageUtils;
import com.fourforfo.common.utils.Query;

import com.fourforfo.fourmall.ware.dao.WareSkuDao;
import com.fourforfo.fourmall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;

@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    OrderFeignService orderFeignService;

    @Autowired
    WareOrderTaskService wareOrderTaskService;

    @Autowired
    WareOrderTaskDetailService wareOrderTaskDetailService;

    @Autowired
    RabbitTemplate rabbitTemplate;


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>();
        String skuId = (String) params.get("skuId");
        String wareId = (String) params.get("wareId");
        if(!StringUtils.isEmpty(skuId)){
            queryWrapper.eq("sku_id",skuId);
        }
        if(!StringUtils.isEmpty(wareId)){
            queryWrapper.eq("ware_id",wareId);
        }
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

    /**
     * 将成功采购的purchaseDetail信息进行入库
     * @param purchaseDetails
     */
    @Override
    public void saveFinishPurchaseDetail(List<PurchaseDetailEntity> purchaseDetails) {
        if(purchaseDetails==null||purchaseDetails.size()==0){
            return;
        }
        //保存要批量更新的WareSku信息
        List<WareSkuEntity> updateWareSkus = new ArrayList<>();
        //保存要批量更新的InsertSku信息
        List<WareSkuEntity> insertWareSkus = new ArrayList<>();
        //保存需要插入的InsertSku的id，用于统一查询pms_sku_info中的sku_name字段，保存InsertSku时需要设置sku_name
        List<Long> insertIds = new ArrayList<>();
        for(PurchaseDetailEntity detailEntity : purchaseDetails){
            //需要判断一下是否在数据库中已存在对应数据，有则更新，无则插入
            Integer count = baseMapper.selectCount(
                    new QueryWrapper<WareSkuEntity>()
                    .eq("sku_id",detailEntity.getSkuId())
                    .eq("ware_id",detailEntity.getWareId()));
            WareSkuEntity wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setSkuId(detailEntity.getSkuId());
            wareSkuEntity.setWareId(detailEntity.getWareId());
            wareSkuEntity.setStock(detailEntity.getSkuNum());
            if(count==null||count==0){//数据库中没有数据就插入一条新数据
                //把id封装起来统一查询设置SkuName字段
                insertIds.add(wareSkuEntity.getSkuId());
                insertWareSkus.add(wareSkuEntity);
            }else{//数据库中有数据就更新
                updateWareSkus.add(wareSkuEntity);
            }
        }
        //改变sku_id和ware_id对应的wms_ware_sku表中的stock数量就行
        if(updateWareSkus.size()>0){
            baseMapper.addStock(updateWareSkus);
        }
        //TODO 本处只对异常捕捉但不抛出，防止事务回滚，后续高级篇采用另一种方法实现
        try{
            if(insertIds.size()>0){
                List<ProductSkuInfoTO> productSkuInfoTOS = productFeignService.listNameByIds(insertIds);
                Map<Long,String> nameMap = new HashMap<>();
                if(productSkuInfoTOS!=null&&productSkuInfoTOS.size()>0) {
                    for (ProductSkuInfoTO productSkuInfoTO : productSkuInfoTOS) {
                        nameMap.put(productSkuInfoTO.getSku_id(), productSkuInfoTO.getSku_name());
                    }
                }
                insertWareSkus = insertWareSkus.stream().map(insertWareSku->{
                    insertWareSku.setSkuName(nameMap.get(insertWareSku.getSkuId()));
                    return insertWareSku;
                }).collect(Collectors.toList());
            }

        }catch (Exception e){
            log.debug("远程调用fourmall的list/batch/name服务发生错误");
            e.printStackTrace();
        }
        if(insertWareSkus.size()>0){
            baseMapper.insertBatch(insertWareSkus);
        }


    }

    /**
     * 根据skuIds查出所有的sku库存信息
     * @param skuIds
     */
    @Override
    public List<SkuStockTO> listBySkus(List<Long> skuIds) {
        if(skuIds!=null&&skuIds.size()>0){
            List<SkuStockTO> skuStockTOS = baseMapper.listBySkus(skuIds);
            return skuStockTOS;
        }else{
            return new ArrayList<>();
        }

    }

    /**
     * 订单服务远程发送请求锁定库存结果
     *  情形一：用户订单30分钟未支付，订单过期，库存自动解锁
     *  情形二：用户生成订单失败，
     *        借助消息队列发送回滚消息给库存服务，
     *        库存服务监听消息进行回滚，
     *        实现分布式事务回滚功能，达到最终一个性。
     * @param vo
     * @return
     */
    @Transactional
    @Override
    public Boolean orderLockStock(WareSkuLockTO vo) {
        //像京东那种分散式仓库，需要按照下单的收货地址，找到就近一个仓库，锁定库存
        //设置库存订单任务实体类
        WareOrderTaskEntity taskEntity = new WareOrderTaskEntity();
        taskEntity.setOrderSn(vo.getOrderSn());
        wareOrderTaskService.save(taskEntity);

        //1. 找到每个商品在哪个仓库都有库存
        List<OrderItemVO> itemVOS = vo.getLocks();
        List<SkuWareHasStock> skuWareHasStocks = itemVOS.stream().map(orderItemVO -> {
            List<Long> wareIds = baseMapper.listWareIdBySkuIdsHasStock(orderItemVO.getSkuId(), orderItemVO.getCount());
            if (wareIds == null || wareIds.size() == 0) {
                throw new NoStockException(orderItemVO.getSkuId());
            }
            SkuWareHasStock hasStock = new SkuWareHasStock();
            hasStock.setSkuId(orderItemVO.getSkuId());
            hasStock.setNum(orderItemVO.getCount());
            hasStock.setWareIds(wareIds);
            return hasStock;
        }).collect(Collectors.toList());

        List<WareOrderTaskDetailEntity> taskDetailEntities = new ArrayList<>();
        for (SkuWareHasStock item : skuWareHasStocks) {
            Boolean skuStocked = false;
            for(Long wareId:item.getWareIds()){
                //成功的话影响的行数为1，失败的话行数返回0
                Integer rowCount = baseMapper.lockSkuStock(item.getSkuId(),item.getNum(),wareId);
                if(rowCount==1){
                    //当前商品锁库存成功
                    skuStocked = true;
                    //初始化库存任务详情类
                    WareOrderTaskDetailEntity taskDetailEntity = new WareOrderTaskDetailEntity();
                    taskDetailEntity.setSkuId(item.getSkuId());
                    taskDetailEntity.setSkuNum(item.getNum());
                    taskDetailEntity.setTaskId(taskEntity.getId());
                    taskDetailEntity.setWareId(wareId);
                    taskDetailEntity.setLockStatus(1);
                    taskDetailEntities.add(taskDetailEntity);

                    break;
                }else{
                    //当前仓库锁失败，尝试锁下一个有该商品的库存
                }
            }
            if (!skuStocked){
                //当前商品尝试锁所有相关仓库都失败，直接抛出异常
                throw new NoStockException();
            }
        }


        //保存订单任务详情信息
        wareOrderTaskDetailService.saveBatch(taskDetailEntities);

        List<StockDetailTO> stockDetailTOS = new ArrayList<>();
        for (WareOrderTaskDetailEntity taskDetailEntity : taskDetailEntities) {
            //收集订单详情类
            StockDetailTO detailTO = new StockDetailTO();
            BeanUtils.copyProperties(taskDetailEntity,detailTO);
            stockDetailTOS.add(detailTO);
        }

        //3.如果执行到这一步，说明所有商品都锁成功了,发送消息队列
        StockLockedTO lockedTO = new StockLockedTO();
        lockedTO.setTaskId(taskEntity.getId());
        lockedTO.setDetails(stockDetailTOS);
        rabbitTemplate.convertAndSend(WareRollbackMQConfig.STOCK_EVENT_EXCHANGE,WareRollbackMQConfig.STOCK_LOCKED_ROUTE,lockedTO);

        return true;
    }

    /**
     * 根据订单信息解锁订单
     * 防止订单服务卡顿，导致订单状态一直不能成功更改，库存消息优先到期，查询订单状态一直为新建状态，无法执行库存解锁，只能拒收消息
     * @param orderTO
     */
    @Override
    public void unlockStock(OrderTO orderTO) {
        String orderSn = orderTO.getOrderSn();
        //查询最新库存解锁状态，防止重复解锁库存
        WareOrderTaskEntity taskEntity = wareOrderTaskService.getOne(new QueryWrapper<WareOrderTaskEntity>().eq("order_sn",orderSn));
        List<WareOrderTaskDetailEntity> detailEntities = wareOrderTaskDetailService.list(
                new QueryWrapper<WareOrderTaskDetailEntity>()
                        .eq("task_id", taskEntity.getId())
                        .eq("lock_status",1));//查询其中状态为锁定的任务项

        if(detailEntities==null||detailEntities.size()==0){
            log.warn("detailEntities为空！");
            return ;
        }
        for (WareOrderTaskDetailEntity detailEntity : detailEntities) {
            detailEntity.setLockStatus(2);//设为解锁状态
        }

        //=============================解锁库存操作==========================
        /**
         * 更新库存
         */
        baseMapper.unLockStocks(detailEntities);

        /**
         * 更新订单任务项状态
         */
        wareOrderTaskDetailService.updateBatchById(detailEntities);


        /**
         * 更新任务状态
         */
        WareOrderTaskEntity updateTaskEntity = new WareOrderTaskEntity();
        updateTaskEntity.setId(taskEntity.getId());
        updateTaskEntity.setTaskStatus(2);//设任务状态为已解锁
        wareOrderTaskService.updateById(updateTaskEntity);
    }

    @Data
    private class SkuWareHasStock{
        private Long skuId;
        private Integer num;
        private List<Long> wareIds;
    }

}